#include <QMetaObject>
#include <QMetaProperty>
#include <QMetaEnum>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QList>
#include "keyobject.h"

typedef void (*WriteKeyCB)(QTextStream &out, char const *key, int value);

void writeKeyCases(QTextStream &out, QMetaEnum metaEnum, WriteKeyCB callback)
{
  // Loop through all Qt::Key enums
  QList<int> check;
  int count = metaEnum.keyCount();
  for (int i = 0; i < count; ++i)
  {
    int value = metaEnum.value(i);
    if (!check.contains(value))
    {
      check.append(value);
      char const* key = metaEnum.key(i);
      callback(out, key, i);
    }
  }
}

/*******************************************************************************
 * Code Generation
 ******************************************************************************/
void writeNotice(QTextStream &out)
{
  out << "/* Code automatically generated by KeyConverter */\n\n";
}

void writeKeyConvertCases(QTextStream &out, char const *key, int value)
{
  out << "    case Qt::" << key << ": return " << value << ";\n";
}

void writeKeyRevertCases(QTextStream &out, char const *key, int value)
{
  out << "    case " << value << ": return Qt::" << key << ";\n";
}

void writeHeader(const QString &classname, QMetaEnum metaEnum, QTextStream &out)
{
  QString incGuard = classname.toUpper() + "_H";
  writeNotice(out);
  out <<
    "#ifndef " << incGuard << "\n"
    "#define " << incGuard << "\n\n"
    "#include <Qt>\n\n"
    "class " << classname << "\n"
    "{\n"
    "public:\n"
    "  static int convertKey(Qt::Key k);\n"
    "  static Qt::Key revertKey(int k);\n"
    "  static const int KeyCount = " << metaEnum.keyCount() << ";\n"
    "};\n\n"
    "#endif // " << incGuard << "\n";
}

void writeSource(const QString &classname, QMetaEnum metaEnum, QTextStream &out)
{
  writeNotice(out);
  out <<
    "#include \"" << classname.toLower() << ".h\"\n\n"
    "int " << classname << "::convertKey(Qt::Key k)\n"
    "{\n"
    "  switch (k)\n"
    "  {\n";
  writeKeyCases(out, metaEnum, &writeKeyConvertCases);
  out <<
    "    default: return -1;\n"
    "  }\n"
    "}\n\n";
  out <<
    "Qt::Key " << classname << "::revertKey(int k)\n"
    "{\n"
    "  switch (k)\n"
    "  {\n";
  writeKeyCases(out, metaEnum, &writeKeyRevertCases);
  out <<
    "    default: return Qt::Key_unknown;\n"
    "  }\n"
    "}\n";
}

/*******************************************************************************
 * Main
 ******************************************************************************/
int main(int argc, char *argv[])
{
  // Check Command Arguments
  if (argc <= 1)
  {
    qFatal("No arguments provided - Expected <Directory> and <Filename>!");
  }
  if (argc > 3)
  {
    qWarning("More than two arguments provided - ignoring all but the first argument.");
  }

  // Open files for writing
  QString directory = argv[1];
  QString classname = argv[2];
  QString basename = classname.toLower();
  QFile headerFile(directory + basename + ".h");
  QFile sourceFile(directory + basename + ".cpp");
  if (!headerFile.open(QIODevice::WriteOnly | QIODevice::Text))
  {
    qFatal("Trouble opening file: %s.h", qPrintable(basename));
  }
  if (!sourceFile.open(QIODevice::WriteOnly | QIODevice::Text))
  {
    qFatal("Trouble opening file: %s.cpp", qPrintable(basename));
  }

  // Find our Qt::Key QMetaEnum
  const QMetaObject &meta = KeyObject::staticMetaObject;
  int propertyIndex = meta.indexOfProperty("KeyEnum");
  QMetaProperty metaProperty = meta.property(propertyIndex);
  QMetaEnum metaEnum = metaProperty.enumerator();

  // Generate Code
  QTextStream headerStream(&headerFile);
  QTextStream sourceStream(&sourceFile);
  writeHeader(classname, metaEnum, headerStream);
  writeSource(classname, metaEnum, sourceStream);

  return 0;
}
